package cn.donting.plugin.spring.boot.starter.plugin.autoconfiguration.web;

import cn.donting.plugin.spring.boot.starter.exception.PluginRuntimeException;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 *  在Mapping 上添加  plugId
 * @author donting
 *  2020-04-17 下午1:44
 */
public class PluginRequestMappingHandlerMapping extends RequestMappingHandlerMapping {


    private final String pluginId;
    private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();

    private boolean classHavePath = false;
    private boolean isClass = false;

    public PluginRequestMappingHandlerMapping(String pluginId) {
        if (pluginId == null) {
            throw new PluginRuntimeException("PluginRequestMappingHandlerMapping pluginId is null",
                    "构建 PluginRequestMappingHandlerMapping 的pluginId 为null");
        }
        this.pluginId = pluginId;
    }

    @Override
    protected void detectHandlerMethods(Object handler) {
        Class<?> handlerType = (handler instanceof String ?
                obtainApplicationContext().getType((String) handler) : handler.getClass());
        RequestMapping annotation = handlerType.getAnnotation(RequestMapping.class);
        if (annotation != null) {
            classHavePath = true;
        } else {
            classHavePath = false;
        }
        super.detectHandlerMethods(handler);
    }
    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        RequestMappingInfo requestMappingInfo = super.getMappingForMethod(method, handlerType);
        isClass = false;

        return requestMappingInfo;
    }
    @Override
    protected RequestMappingInfo createRequestMappingInfo(
            RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {

        String[] paths = resolveEmbeddedValuesInPatterns(requestMapping.path());
        String[] originalPath = new String[paths.length];
        for (int i = 0; i < originalPath.length; i++) {
            originalPath[i] = paths[i];
        }
        //plugId 前缀+原始路径
        if (classHavePath && isClass || !classHavePath) {
            for (int i = 0; i < paths.length; i++) {
                if (paths[i].substring(0, 1).equals("/")) {
                    paths[i] = "/" + pluginId + paths[i];
                } else {
                    paths[i] = pluginId + "/" + paths[i];
                }
            }
        }
        paths = Arrays.copyOf(paths, paths.length + originalPath.length);
        System.arraycopy(originalPath, 0, paths, originalPath.length, originalPath.length);
        isClass = true;
        RequestMappingInfo.Builder builder = RequestMappingInfo
                .paths(paths)
                .methods(requestMapping.method())
                .params(requestMapping.params())
                .headers(requestMapping.headers())
                .consumes(requestMapping.consumes())
                .produces(requestMapping.produces())
                .mappingName(requestMapping.name());
        if (customCondition != null) {
            builder.customCondition(customCondition);
        }
        return builder.options(this.config).build();
    }

    @Override
    @SuppressWarnings("deprecation")
    public void afterPropertiesSet() {
        this.config = new RequestMappingInfo.BuilderConfiguration();
        this.config.setUrlPathHelper(getUrlPathHelper());
        this.config.setPathMatcher(getPathMatcher());
        this.config.setSuffixPatternMatch(useSuffixPatternMatch());
        this.config.setTrailingSlashMatch(useTrailingSlashMatch());
        this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
        this.config.setContentNegotiationManager(getContentNegotiationManager());
        super.afterPropertiesSet();
    }
}
